Set a referenced variable to a newly initialized class - php

I have a method, which takes a reference
// CarService.php
public function getCars(&$carCollection = null)
{
$promise = // guzzle request for getting all cars would be here
$promise->then(function (ResponseInterface $response) use (&$carCollection) {
$cars= json_decode($response->getBody(), true);
$carCollection= new CarCollection($cars);
});
}
However, when accessing the collection and trying to reuse it, I'm getting the error
Argument 1 passed to {placeholder} must be an instance of {placeholder}, null given
I know that the reason for this is, that the constructor returns nothing, but how can I still assign my variable to a new instance of the CarCollection (which extends Doctrine's ArrayCollection)
I even tried it with a static method as a work around
// CarCollection.php
public static function create(array $cars): CarCollection
{
$carCollection = new CarCollection($cars);
return $carCollection;
}
// CarService.php
public function getCars(&$carCollection = null)
{
$cars = // curl request for getting all cars would be here
$carCollection = CarCollection::create($cars)
}
but it's still null. Why is that? How can I set a referenced variable to a new class?
I access the method like this
$carService = $this->get('tzfrs.vehicle.services.car');
$carCollection = null;
$promises = [
$carService->getCars($carCollection)
];
\GuzzleHttp\Promise\unwrap($promises);
var_dump($carCollection); // null
When I set the reference directly, eg.
// CarService.php
public function getCars(&$carCollection = null)
{
$carCollection = new CarCollection([]);
}
it works without any problems. Seems like the callback is somehow the problem.
Whoever downvoted this, can you please elaborate why and why you voted to close?

I might be misunderstanding the question, but you should be able to modify an object when passing by reference. See here for an example: https://3v4l.org/KtFvZ
In the later example code that you added, you shouldn't pass $carCollection by reference, the & should only be in the method/function defintion, not provided when you call it. I don't think that is your problem though, that should be throwing an error in php7.

Related

Calling methods on object gets 'call to member function on array' error

I inherited this project from my predecessor, and he was way overqualified. A lot of stuff he wrote goes over my head. But as far as vanilla php goes, I'm pretty confident, and can't for the life of me figure out why the application thinks the object I created is an array. Maybe I don't actually know anything. You tell me.
use via\zoom\Bulletin;
use via\zoom\DatabaseConnection;
require_once('includes/config.php');
require_once(CORE .'sql.php');
require_once(CORE . 'model.php');
require_once(CORE . 'bulletin.php');
// If we've passed the validation step we can guarantee we have a valid $active_user
validate();
//run if a page deletion has been requested
if (isset($_GET['delpage'])) {
$del = $_GET['delpage'];
$bulletin = new Bulletin;
$bulletin = Bulletin::get($del);
if(!empty($bulletin))
{
$bulletin->delete();
/*
So.
For some reason, the above object is cast as an array.
If you try to cast it as an object, it defaults to stdClass.
On the left we have a method complaining that it can't work outside of its class. Hard stop, array to method exception.
On the right we have an object with all the right data, but set to the wrong class, so it can't find the delete method at all. Hard stop, undefined method exception.
*/
//this is the workaround, pulled the script straight from the delete method in the model class
/*$dbh = DatabaseConnection::get();
$query_string = "DELETE FROM brochure_generator_bulletin WHERE id = $del";
try {
$dbh->query($query_string);
//return true;
} catch (\Exception $e) {
//return false;
}*/
}
header('Location: bulletins');
exit();
}
Here's the get method from the Bulletin class, extends Model--
public static function get( ...$ids )
{
$matches = parent::get( ...$ids );
foreach( $matches as &$match )
{
$match->content = json_decode( $match->content );
}
return $matches;
}
And here's the delete method from the Model Class:
public function delete()
{
if (isset($this->id)) {
$dbh = DatabaseConnection::get();
$query_string = "DELETE FROM {$this->table_name} WHERE id = \"{$this->id}\"";
try {
$dbh->query($query_string);
return true;
} catch (\Exception $e) {
return false;
}
}
return false;
}
What am I missing? Is he using a framework I'm not familiar with? I'm utterly grasping at straws here, and at this point my options are grab all the method scripts and stick them where they need to be inline, or just starting over from the ground up.
You don't need to create a new Bulletin object before using the static get() method, so you can remove this:
$bulletin = new Bulletin;
That $bulletin variable is immediately overwritten by the next line anyway.
$bulletin = Bulletin::get($del);
get() takes one or more ids and returns an array of one or more corresponding objects. You're giving it one id and expecting one object back, but it's still going to return that object inside an array. You just need to get the object out of the array so you can call its delete method.
if(!empty($bulletin))
{
$bulletin = reset($bulletin); // get the first item in the array
$bulletin->delete();
You could also review the model and see if it has a different method that returns a single object rather than an array of objects.

PHP Slim Get route placeholder in a container

Is it possible get the value of a route placeholder within a Slim container? I know I can access the placeholder by adding a third parameter to the request but I'd like to have it injected so I'm not assigning it on each request.
I've tried $app->getContainer('router') but I can't seem to find a method to actually pull the placeholder value.
Example:
$app = new Slim\App;
$c = $app->getContainer();
$c['Controller'] = function() {
$userId = // how do I get the route placeholder userId?
return new Controller($userId);
};
$app->get('/user/{userId}','Controller:getUserId');
class Controller {
public function __construct($userId) {
$this->userId = $userId;
}
public function getUserId($request,$response) {
return $response->withJson($this->userId);
}
}
Without some 'hacky' things this will not work because we have no access on the request object build by slim, while the controller get constructed. So I recommend you to just use the 3rd parameter and get your userid from there.
The 'hacky' thing would be todo the same, what slim does when you execute $app->run(), but if you really want todo this, here you'll go:
$c['Controller'] = function($c) {
$routeInfo = $c['router']->dispatch($c['request']);
$args = $routeInfo[2];
$userId = $args['userId'];
return new Controller($userId);
};
Note: slim3 also urldecoded this values so may do this as well urldecode($args['userId']) Source
create a container wrapper and a maincontroller then extend all your controller from your maincontroller, then you have access to the container.
here is how i solved this problem:
https://gist.github.com/boscho87/d5834ac1ba42aa3da02e905aa346ee30

Multiple API Calls in a Class

I am trying to make multiple API requests and I have to make the request in different functions that are within a class like so:
class exampleClass
{
function callFunction1 () {
// stuff that makes a call
return $json;
}
function printStuffOut() {
$jsonStuff = $this->callFunction1();
$$jsonStuff->{'result'}[0]->{'fieldName'};
}
function printStuffOut2() {
$jsonStuff = $this->callFunction1();
$jsonStuff->{'result'}[0]->{'fieldName'};
}
}
Am I making two separate API calls?
If I am, is there a way to store that API call information say in an array then use that array in all the other functions in my class?
Answer to first question: Yes you are, each time the method is called it executes all its definition again.
Answer to second question: Yes there is, so called member properties. You can read up about them in the PHP manual here: PHP Manual: Properties
You are making two API calls, but you don't have to.
You can put the contents of a call into a member variable in the class with a default value of NULL, and if you want, you can check if that member variable is NULL before making an API call. For example;
class exampleClass
{
private $api_json = NULL;
private function call_api()
{
if(is_null($this->api_json))
{
$json = // result of api call;
$this->api_json = $json;
}
return $this->api_json;
}
public function printStuffOut() {
$jsonStuff = $this->call_api();
$jsonStuff->{'result'}[0]->{'fieldName'};
}
public function printStuffOut2() {
$jsonStuff = $this->call_api();
$jsonStuff->{'result'}[0]->{'fieldName'};
}
}
You can use following class to achieve multiple API simultaneously/instantly/at once.
Click here to get a class.
How to use it?
Step 1: Get object.
//SANI: GET DATA
$obj = new multiapi();
Step 2: Make a multiple GET Requests.
$obj->data = array(YOUR_URL_1,YOUR_URL_2, OUR_URL_3);
$result = $obj->get_process_requests();
print_r($result);
Step 3: Make a multiple HTTP POST Requests.
//SANI: Request with params only
$obj->data[0]['url'] = 'YOUR_URL_ONE';
$obj->data[0]['post'] = array();
$obj->data[0]['post']['param_1'] = 'param_value_1';
$obj->data[0]['post']['param_2'] = 'param_value_2';

When should one pass an object as a parameter vs instantiating?

I am curious about the best practices and any performance or other considerations relating to passing an instance of an object as a parameter to another function in the same class vs creating another instance of that object in the new function. Here's a quick example:
Option 1: Pass both instance of Trainee AND TraineeController to other functions
protected function startTraining($traineeID) {
$traineeController = new TraineeController();
$trainee = $traineeController->findTrainee($traineeID);
$this->initializeTraining($trainee, $traineeController);
$this->doSomeOtherStuffWithTrainee($trainee, $traineeController);
return Redirect::back()->with('trainee', $trainee);
}
protected function initializeTraining($trainee, $traineeController) {
$trainee->blah1 = 'red';
$trainee->blah2 = 'blue';
$propertiesToUpdate = [
'blah1' => $trainee->blah1,
'blah2' => $trainee->blah2
];
$traineeController->updateTrainee($trainee->traineeID, $propertiesToUpdate);
}
Option 2: Pass $trainee ONLY, instantiate a new TaineeController each time
protected function startTraining($traineeID) {
$traineeController = new TraineeController();
$trainee = $traineeController->findTrainee($traineeID);
$this->initializeTraining($trainee);
$this->doSomeOtherStuffWithTrainee($trainee);
return Redirect::back()->with('trainee', $trainee);
}
protected function initializeTraining($trainee) {
$trainee->blah1 = 'red';
$trainee->blah2 = 'blue';
$propertiesToUpdate = [
'blah1' => $trainee->blah1,
'blah2' => $trainee->blah2
];
$traineeController = new TraineeController();
$traineeController->updateTrainee($trainee->traineeID, $propertiesToUpdate);
}
In the above I need to pass $trainee across all functions each time instead of creating a new trainee from $traineeID because some other stuff goes on behind the scenes during the 'training' process that would otherwise be lost before relevant data is saved to the db. However, this is not required for TraineeController - I can either pass it as a parameter or instantiate a new TraineeController as much as I want. Which is the better choice?
I saw this question relating to C#, where the accepted answer was that passing an entire object is usually more efficient and instantiating another one because you are passing by reference. Does this hold true for PHP? Ie is the most efficient approach to pass the entire object by reference to required functions using &?
There is nothing wrong with passing an object as reference, but note that php expects that your function argument needs to expect a reference rather than just passing a variable by reference (php docs). php 5.4.0 will even raise a fatal error if this is not respected:
right:
protected function initializeTraining($trainee, &$traineeController) {}
$this->initializeTraining($trainee, $traineeController);
wrong:
protected function initializeTraining($trainee, $traineeController) {}
$this->initializeTraining($trainee, &$traineeController);
Passing objects by reference will in most cases have better performance than initiating the object again, but passing by reference could become tricky if your object has its own properties:
class TraineeController {
$fooCalled = false;
function foo(){ $this->fooCalled = true; }
function isFooCalled(){ return $this->fooCalled; }
}
$traineeController = new TraineeController();
$traineeController->foo();
//&$traineeController->isFooCalled() will be different from
//new TraineeController()->isFooCalled().

Run a method if it has not already ran with the current class?

I have a class that I am writing and I have a method that I would like to run once per initiation of the class. Normally this would go in the construct method, but I only need it to run when I call certain methods, not all.
How would you all recommend I accomplish this?
Create a private property $methodHasBeenRun which has a defualt value of FALSE, and set it to TRUE in the method. At the start of the method, do:
if ($this->methodHasBeenRun) return;
$this->methodHasBeenRun = TRUE;
You didn't specify exactly why you only want to run a given method once when certain methods are called, but I am going to make a guess that you're loading or initializing something (perhaps data that comes from a DB), and you don't need to waste cycles each time.
#DaveRandom provided a great answer that will work for sure. Here is another way you can do it:
class foo {
protected function loadOnce() {
// This will be initialied only once to NULL
static $cache = NULL;
// If the data === NULL, load it
if($cache === NULL) {
echo "loading data...\n";
$cache = array(
'key1' => 'key1 data',
'key2' => 'key2 data',
'key3' => 'key3 data'
);
}
// Return the data
return $cache;
}
// Use the data given a key
public function bar($key) {
$data = $this->loadOnce();
echo $data[$key] . "\n";
}
}
$obj = new foo();
// Notice "loading data" only prints one time
$obj->bar('key1');
$obj->bar('key2');
$obj->bar('key3');
The reason this works is that you declare your cache variable as static. There are several different ways to do this as well. You could make that a member variable of the class, etc.
I would recommend this version
class example {
function __construct($run_magic = false) {
if($run_magic == true) {
//Run your method which you want to call at initializing
}
//Your normale code
}
}
so if you do not want to run it create the class like
new example();
if you want
new example(true);

Categories