Maybe I am searching wrong, but I'm unable to find how to specify an Object Class in a string. Example:
I keep a list of json sites in a database, perform a foreach loop to retrieve a specific item from each site. However, each site has a different structure.
SITE 1: $result->Food->Price->Today;
SITE 2: $result->Pizza->Slice->discount;
I am trying to keep "$result->Pizza->Slice->discount" in a variable (specified in database) and return it from the class.
Sounds easy, but I'm new to class objects and all I find is how to create an object from an array.
Store this value into your database:
serialize(['Pizza', 'Slice', 'discount']);
When reading the value, unserialize it:
unserialize($value_from_db);
To retrieve the value from the JSON object use this simple function:
function retrieve_value($object, $trail) {
foreach ($trail as $name)
if (isset($object->$name))
$object = $object->$name;
else
throw new Exception("Object does not have the property $name");
return $object;
}
So you have something like this:
$value = retrieve_value(json_decode($json), unserialize($db_value));
Do not use eval(), because it is evil.
A dirty way to do this would be to json encode and decode.
$array = array([Your array]);
$obj = json_decode(json_encode($array));
Haven't tested the code though.
I guess you could store the pathing inside the database then use "eval()" with the path on the object. Just make sure you know no one can alter the pathing because eval is dangerous with un-sanitized code!
eval("\$value = \$result->{$pathvar};");
I didn't test that, but something of the sort. Of course $pathvar would be the value of the path coming from the database, whatever variable that's stored in.
Related
I'm writing a piece of data processing code that often has to set a property on a target object to the value of a property of a source object, but only if that property exists in the source object, like so:
if (property_exists($source, 'something')) {
$target->other = $source->something;
}
(I don't use isset() because null values should also be passed on.)
Repeating the above all over the code would be a pain. Right now I'm using this function to do it:
function set_if_exists($target, string $targetProperty, $source, string $sourceProperty)
{
if (property_exists($source, $sourceProperty)) {
$target->$targetProperty = $source->$sourceProperty;
}
}
which for the example above would be
set_if_exists($target, 'other', $source, 'something');
Is there a way to achieve the same without having to name the properties as strings? Ideally I would like to be able to do something like
set_if_exists($target->other, $source->something);
but I have no idea how to achieve this. Is it even possible?
array_key_exists only works on arrays, you are using objects. You should use the property_exists function to know if an object has a specific property.
You cannot do this the way you want to because a non-existing property will always return null, so there is no way to know if the property exists or not from the value.
I have concluded that this is not possible. There does not seem to be any mechanism in PHP that would allow you check for the existence of a (potentially deep) property, the way that the isset() builtin or ?? can.
I've been using procedural php for a long time but am still learning OOP. I was able to find a bit of code online that is in the form of a class and I am able to call a new instance of the class and use var_dump on a string to have it print JSON data to a web page. I can look at the results of the var_dump and see that it's returning exactly what I want. I'm then able to use json_decode on the same string to turn it into and associative array and then I can echo the values from within the class. The problem is, I need to use the array values in more code - it's great that I can confirm it by printing it to a web page but I need to use it... but I'm getting errors that state the array is undefined once I try to access it outside of the class.
I'm using this line to convert the data into an array:
$response_array = json_decode($body, true);
I've already confirmed that this works within the class by using this code to print some of the data:
echo $response_array['amount'];
and it works - I see it on the web page.
I've been using this code to create the new instance of the class:
$fdata = new FData();
$fdata->request($order_total, $cc_exp, $cc_number, $cc_name, $order_id, $customer_id);
(the function named 'request' is defined as a public function inside the class)
After that, I want to grab the $response_array so that I can store the returned data into a transactions table, i.e something like this:
$r = mysqli_query($dbc, "CALL add_transaction($order_id, $response_array['transaction_type'], $response_array['amount'], $response_array['exact_resp_code'], $response_array['exact_message'], $response_array['bank_resp_code'], $response_array['bank_message'], $response_array['sequence_no'], $response_array['retrieval_ref_no'], $response_array['transaction_tag'], $response_array['authorization_num'])");
but I keep getting an error saying that the array values are undefined.
Things I have already tried (and which failed) include:
Defining the variables as public inside the class, setting their value in the class, and then calling them outside the class...
public $amount = $response_array['amount'];
then using $amount in my procedure CALL --- I still get the error saying that $amount is undefined.
Using 'return', as in
return $response_array;
and still the error saying the values are undefined.
I tried embedding the entire rest of my code within the class, just copy/paste it in right after the json_decode... but for some reason it can't seem to then make the database calls and other things it needs to do.
I've been reading about __construct but I'm not sure if it applies or how to use it in this case...
I want to stress that I AM able to see the results I want when I use var_dump and echo from within the class.. I need to access the array created by json_decode OUTSIDE of the instance of the class.
Any advice would be greatly appreciated. Thanks.
Assuming your FData::request method ends with something like this...
$response_array = json_decode($body, true);
return $response_array;
and you call it like this...
$response_array = $fdata->request(...);
You should then be able to use $response_array in the calling scope.
Extra note; you should be using prepared statements with parameter binding instead of injecting values directly into your SQL statements.
I'm writing a PHP script that accesses two different data objects - one is backed by a DB, the other contains session data. They both contain the same fields - i.e. the data that is in the session object will make its way into the DB once it is validated.
I'm trying to write a function that will first check the db-backed object for a value, then check the session-data-backed object for a value. It's not working like I quite expect, however. Here is what I have.
<?php
function check_cache($field){
// $App is the DB backed object, $data is the session object
return $app->$field ? $app->$field : $data->$field ? $data->$field : '';
}
?>
I'd like to be able to call the function like this:
<input type="text" value="<?php echo check_cache('address'); ?>" />
...but the function always returns nothing. When I replace the function call with the actual inline code, substituting $field with the field name I want, it works. What am I missing?
You should ensure that $app and $data exist within the scope of the function (potential by passing them as parameters, I'll leave that to you). Try this:
<?php
function check_cache($field){
// $App is the DB backed object, $data is the session object
return $app->${$field} ? $app->${$field} : $data->${$field} ? $data->${$field} : '';
}
?>
You should probably extend this though to ensure that $app->$$field exists, etc.
Edit:
This is pretty wrong, as pointed out by bob-the-destroyer in the comments. $app->$field is all you need. Just make sure the scope is right, and the members you want aren't private. Apologies.
You can try something like this:
<?php
function check_cache($field){
global $app, $data;
// $App is the DB backed object, $data is the session object
$av = eval("\$app->".$field);
$dv = eval("\$data->".$field);
return $av ? $av : $dv ? $dv : '';
}
?>
I'd be tempted to say it's a scope issue, $app + $data don't exist inside the function, only outside. Extend the function to take two extra params, and pass in the $app + $data objects that way.
Addendum
This won't entirely fix the problem, you'll still need to access the fields of the object, depending on your class structure, if it's properties are private, will make some of the alternate answers not work, as the field won't be accessible via the obj->field syntax.
I was previously mostly scripting in PHP and now considering getting "more serious" about it :)
I am working on a hiking website, and I needed to put some values into an object that I then try to pass back to the calling code.
I tried doing this:
$trailhead = new Object ();
But the system sort of barfed at me.
Then I didn't declare the object at all, and started using it like this:
$trailhead->trailhead_name = $row['trailhead_name'];
$trailhead->park_id = $row['park_id'];
That seemed to work reasonably ok. But there are at least 3 problems with this:
Since that code gets executed when getting things from the database, what do I do in case there is more than one row?
When I passed the $trailhead back to the calling code, the variables were empty
I actually am maybe better off making a real object for Trailhead like this:
class Trailhead
{
private $trailhead_name;
private $park_id;
private $trailhead_id;
public function __construct()
{
$this->trailhead_name = NULL;
$this->park_id = NULL;
$this->trailhead_id = NULL;
}
}
What do people generally do in these situations and where am I going wrong in my approach? I know its more than one place :)
$trailheads[] = $trailhead;
I'd do a print_r() of $trailhead to check that it's what you expect it to be. The default object type in PHP is going to be stdClass.
Yes, that's going to be better, as it'll allow your Trailhead objects to have functions. The way you're currently doing it is basically taking advantage of none of PHP's object functionality - it's essentially an array with slightly different syntax.
I think you should get in "contact" with some of the basics first. Objects in PHP have sort of a history. They are a relative to the array and there are two sorts of objects:
data-objects
class objects
data objects are called stdClass and that's actually what you were initially looking for:
$trailhead = new Object();
in PHP is written as:
$trailhead = new stdClass;
(with or without brackets at the end, both works)
You then can dynamically add members to it, like you did in the second part without declaring the variable (that works, too in PHP):
$trailhead->trailhead_name = $row['trailhead_name'];
$trailhead->park_id = $row['park_id'];
If you want to more quickly turn $row into an object, just cast it:
$trailhead = (object) $row;
That works the other way, too:
$array = (array) $trailhead;
As you can see, those basic data based objects in PHP do not hide their relationship to the array data type. In fact you can even iterator over them:
foreach($trailhead as $key=>$value) {
echo $key, ' is ', $value, "<br>\n";
}
You can find lots of information about this in the PHP manual, it's a bit spread around, but worth to know the little basics before repeating a lot of names only to pass along the data that belongs together.
Next to these more or less stupid data objects, you can code complete classes that - next to what every stdClass can do - can have code, visibility, inheritance and all the things you can build nice stuff from - but this can be pretty complex as the topic is larger.
So it always depends on what you need. However, both type of objects are "real objects" and both should work.
class myClass {
function importArray(array $array) {
foreach($array as $key=>$value) {
if(!is_numeric($key)) $this->$key=$value;
}
}
function listMembers() {
foreach($this as $key=>$value) {
echo $key, ' is ', $value, "<br>\n";
}
}
}
$trailhead = new myClass();
$trailhead->importArray($row);
echo $trailhead->park_id;
Keep in mind that instead of creating a set of objects that merely does the same in each object (store data), you should just take one flexible class that is handling the job flexible (e.g. stdClass) because that will keep your code more clean.
Instead of coding a getter/setter orgy you can then spend the time thinking about how you can make the database layer more modular etc. .
Just pass back an array:
$trailhead = array(
'trailhead_name' => $row['trailhead_name'],
'park_id' => $row['park_id'],
'trailhead_id' => $row['trailhead_id'],
)
Then either access it like:
$trailhead['park_id'];
or use the nifty list() to read it into variables:
list($trailhead_name, $park_id, $trailhead_id) = $trailhead;
The thing is that you have classes and then you have the database data. When you create an object how do you set the objects properties to contain the data in the database ?
I saw something like this and I'm wondering if this is really the best way to do it. I'm sure this is a fairly common issue, but I don't know what are the most accepted solutions on how to handle it.
In this example when the object is created you pass an id as a parameter and then you run a query to the database with the id and you assing the returned values to the object properties. I don't have much PHP experience and haven't seen this used much.
Is this an acceptable way to achieve this purpose ? Is there a better or more accepted way ?
public function __construct($id = null){
if($id != null){
$sql = "SELECT *
FROM users
WHERE user_id = $id";
$res = Db::returnRow($sql);
// $res contains an associative array with database columns and values
if($res){
$this->user_id = $res['user_id'];
$this->user_name = $res['user_name'];
//and so on...
}
}
}
Could somebody provide some sample code or pseudocode to illustrate what is the correct way to do this ?
It could be an acceptable way for a homework maybe. But architecturaly it is not.
Your class that is representing your business data (a user in your example) must be loosely coupled with your database access logic. In the end the PHP class acting as a user should not be aware that the data come from a database, a file or any other resource. Following that you will be able to reuse your user php class in other projects without having to change anything to it! If you have your data access logic inside it you are stuck.
Conclusion: I would suggest to read some resources on Design Pattern (in your situation take a look at DAO pattern) ;) Hint: the one from Head First series is extremely accessible and enjoyable.
You could create a function to do this for you automatically, by looping over the associative array's key/value pairs. Or you could look into using an ORM library.
Yes, you can semi-automate this by having a parent class all objects inherit from. On load, it queries, "SHOW FIELDS FROM [my tablename]" and populates an associative array with the names. If an id has been passed in, it looks for a valid object in that table with that id and assigns the values to the array.
Side note: don't pass your id directly into your query like that. Parametize the sql and wrap a function around any user input to sanitize it.
If it's mysql, you can just do:
$obj = mysql_fetch_object($query);
PDO the ability to use arbitrary classes as the target for a fetch, but beware that they assign the variable data before running the constructor:
$pdo->query($stmt, PDO::FETCH_CLASS, "MyClass", array('foo'=>'bar'));
...where the final parameter contains arguments for your class constructor.