I have an object which pulls back details about the site. Basically the name address etc. One of the fields it pulls back is IDCounty. see below. I then want to feed this into a new instance of another object and it automatically give me the county name. Here is what i have
so my system object
class System{
private $db;
public $Ad1;
public $Ad2;
public $County;
public $IDSystem;
//connect to database
public function __construct($IDSystem ='1') {
$this->db = new Database();
$this->IDSystem = $IDSystem;
}
//get address
public function ContactInfo() {
$Query = sprintf("SELECT BSAd1, BSAd2, IDCounty FROM BusinessSettings WHERE IDBusinessSettings = %s",
GetSQLValueString($this->IDSystem, "int"));
$Query = $this->db->query($Query);
$Result = $this->db->fetch_assoc($Query);
$this->Ad1 = $Result['BSAd1'];
$this->Ad2 = $Result['BSAd2'];
$County = new County($Result['IDCounty']);
$this->County = $County;
}
//end address
}
As you can see this object is calling another County and setting the $County to $this->County. My details for that are below
class County {
public $IDCounty;
private $db;
public function __construct($IDCounty) {
$this->db = new Database();
$this->IDCounty = $IDCounty;
$Query = sprintf("SELECT CountyName FROM County WHERE IDCounty = %s", GetSQLValueString($this->IDCounty, "int"));
$Query = $this->db->query($Query);
$County = $this->db->fetch_assoc($Query);
return $County['CountyName'];
}
}
When I'm calling the object I call it like so
$SiteDetails = new System();
$SiteDetails->ContactInfo();
echo $SiteDetails->Ad1;
echo $SiteDetails->County;
Im getting an error from error reporting on echo $SiteDetails->County; which says
"Catchable fatal error: Object of class County could not be converted to string"
After Googling this error I see the System class is having trouble converting getting the county name from the County class and converting it to $this->County
Unfortunately for me I'm not sure how to fix it though. I thought I could return a value from a function upon instantiation but it seems I'm wrong. Please help. thanks guys.
Whatever return statements a constructor may contain, they will be ignored. A constructor will, by definition, return a new instance of a given class. Read the docs. Though the signature of __construct() shows a return-type of void, you should think of it as void* (a void pointer). That means, it returns a reference to the newly created object. You should replace the return statement with soimething like:
$this->stringValue = $County['CountyName'];
And, because of many other magic-methods, you can implement another one to use a class as a string: the magic __toString method:
public function __toString()
{
return (string) $this->stringValue;//the cast is optional
}
As ever, read through the documentation on some of the other magic-methods. They're quite useful, though I must add, they are/can be slow. you could, equally well implement a getter:
//assuming this is in your constructor:
$this->stringValue = $County['CountyName'];
public function getContyName($default = '')
{
if (!$this->stringValue)
{
return $default;
}
return $this->stringValue;
}
Now, thanks to the $default argument, I can do this:
$county = new County();//not passing any values
echo $county->getCountyName('unknown');//will echo unknown
echo $county->getCountyName();//echoes empty string, because that's the default value of $default...
$county2 = new County('Foobar');
echo $county2->getCountyName('unknown');//echoes Foobar, county is known...
Other than that, your sprintf call isn't making much sense to me. The format used is %s, so I'd expect you pass a string. In fact you're passing GetSQLValueString($this->IDCounty, "int"), which is a the return value of a call to a global function (terrible code smell BTW). Since you're passing 'int' as an argument there, I'd expect the return value to be an int, so why not %d in your sprintf format?
There are many, many other things that need work here (for example: creating a new Database instance without params? really? Why not query using a DB object? why use a global function? Why aren't you using prepared statements? ... Please, read up some more on OOP
Implement the method __toString() in County class.
Example:
public function __toString()
{
return $this->foo;
}
Related
I'm attempting to fetch, convert and save a value in a models' constructor in Laravel 5.2. The reason being that it's saved in the database as hex, and I need to convert it to binary pretty often, and would like to do it once and save the result in a class attribute. But I can't seem to be able to fetch the value from $this in the constructor.
Here's a excerpt of what I'm working with, guid is a field in my table.
class Person extends Model {
private $bGuid = null;
public function __construct(array $attributes = []) {
parent::__construct($attributes);
$this->ad = Adldap::getProvider('default');
$this->bGuid = hex2bin($this->guid);
}
public function getName(){
$query = $this->ad->search()->select('cn')->findBy('objectGUID', $this->bGuid);
return $query['attributes']['cn'][0];
}
}
The $this->ad attribute executes as expected, but $this->bGuid does not. Some debugging shows that $this->guid when referenced in the constructor returns null. While if referenced in the getName() method directly works just fine.
My intermediate solution is creating a new function and just call $this->getbGuid(), thus making me a bit more satisfied with the DRY-ness, but it still has to convert it each time it is called.
I would appreciate it if anyone could tell me what's going wrong, so I could improve the code :)
Try to override another method from Model: newFromBuilder().
This is the one that is executed once the data is retrieved from the DB, not the __construct() one:
class Person extends Model {
private $bGuid = null;
public function newFromBuilder($attributes = [], $connection = null)
{
$model = parent::newFromBuilder($attributes, $connection);
$model->bGuid = hex2bin($model->guid);
return $model;
}
}
Note, that inside the overridden method you refer to the object as $model (instead of $this), and it has to return the $model object at the end.
With the following code, PDO won't return my $parcel as an object, but as an array. There for when i try to call my objects function, it will fail. Every example i have found has done this, in a similar way to mine. What am i doing wrong?
$statement = $this->connection->query($query);
$statement->setFetchMode(PDO::FETCH_CLASS, 'Parcel');
while ($parcel = $statement->fetch()) {
echo $parcel->hello();
}
The Parcel class, if interested.
class Parcel {
public $id;
public $parcel_number;
public $registred_at;
public $shipped_by;
public $shipped_at;
function __construct($parcel_number)
{
$this->parcel_number = $parcel_number;
}
public function hello(){
return "World";
}
}
And im using folder structures to structure the code and an Autoloader, that can probaly affect the PDO's way of calling the object.
While this is the error code
Call to a member function info() on a non-object
This error means the object is null or not instantiated properly, you might need to specify full path to your class.
For example
$statement->setFetchMode(PDO::FETCH_CLASS, 'app\model\Parcel');
This question is very similar to the one already asked here: PDO using PDO::FETCH_PROPS_LATE and __construct() call?
However, the accepted answer in that question doesn't really answer how to pass fetched values into the constructor of the class, if that's even possible. Here's some reduced code:
<?php
class Course {
private $name;
private $code;
private $prerequisites;
public function __construct($name, $code) {
if(!is_string($code) || mb_strlen($code) !== 7) {
throw new \InvalidArgumentException('Invalid code.');
}
$this->name = $name;
$this->code = $code;
$this->prerequisites = array();
}
public static function getAllCourses() {
$sql = 'SELECT `name`, `code` FROM `courses` ORDER BY `code`;';
$db = new \PDO(' ... ', DB_USER, DB_PASS);
$stmt = $db->query($sql);
$stmt->setFetchMode(\PDO::FETCH_CLASS | \PDO::FETCH_PROPS_LATE, 'Course', array('name', 'code'));
return $stmt->fetchAll();
}
}
?>
The problem with this code is that the literal strings "name" and "code" get passed to the constructor. However I want to pass the actual values of the name and code to the constructor (obviously).
Can this be done? If yes, how? Do I use bindColumn()?
basically, any PDO::fetch_something() method WILL ASSIGN THE PROPERTIES.
so you just ned properties, that have the same name as the columns, as you have it correctly.
you don't even need getters or setters or a constructor!
you can pass values to the constructor of the class like this:
$stmt->setFetchMode(\PDO::FETCH_CLASS,
'Course',
array(<value of col1>, <value of col2>));
note, that you do not pass the name of the columns, but the values, in the array!
but that is tricky! be sure to have set the fetchMode properly!
if \PDO::FETCH_PROPS_LATE is set it will FIRSTLY call the __construct and SECONDLY set the properties. so the values you have passed to the __construct via the array will be overwritten.
if you haven't set \PDO::FETCH_PROPS_LATE it will do it reverse.
just try it.
Looking at the following code, I see the constructor is returning a value. I thought that constructors only return objects. Can someone tell me what am I missing?
public function __construct($username = null, $password = null){
$urlLogin = "{$this->apiHost}/login/$username";
$postData = sprintf("api_type=json&user=%s&passwd=%s",
$username,
$password);
$response = $this->runCurl($urlLogin, $postData);
if (count($response->json->errors) > 0){
return "login error";
} else {
$this->modHash = $response->json->data->modhash;
$this->session = $response->json->data->cookie;
return $this->modHash;
}
}
Indeed you are correct. Nothing can be done with the return value of a constructor (aside from using the Object it created).
So no, you aren't missing anything, it's the developer who wrote that code who is.
It is technically possible to use return values from constructors, if you call the function directly
$obj->__construct();
That would allow you to use the constructor's return value. However, that is highly uncommon and fairly not recommended.
You can do whatever you want with the return value of a constructor, so it's not true that "Nothing can be done with the return value of a constructor (aside from using the Object it created)." The return value of a constructor is not the object "it" created. The constructor does not create objects (the new keyword does). The return value of a constructor is the same as that of any other function: whatever you choose to return. Further, it is also false that an object already has to exist in order to call its constructor. This is perfectly valid:
$parent_constructor_return_value = parent::__construct();
For example:
abstract class MyBase {
function __construct () {
return "Hello, world.";
}
}
class MyDerived extends MyBase {
function __construct () {
echo parent::__construct();
}
}
new MyDerived(); // prints "Hello, world."
While this is possible, I can't conceive of a scenario in which it would be best practice. After all, you could always call a method other than parent::__construct() to get your value, and all you lose is obscurity. I suppose it could be used as a way of error-handling--there are two other ways to accomplish the same thing:
Throw Exceptions in the parent constructor and catch them in your derived constructor.
Set properties in the parent constructor indicating that an error happened, and then check the state of those properties in the derived constructor.
If an error in a parent constructor is not exceptional, he might have decided to have the parent constructor return error values, rather than storing transient error information as object properties. Of course, then the only reason to name the parent's method __construct is if the parent class is not abstract but can itself be instantiated--but in that context, the returned error messages would never be seen. So, bad pattern; bad. Constructors are not intended to return values, which means you're opening an architectural can of worms by leveraging this mechanism.
A constructor returns nothing, but you can return from it (stopping the method execution at a point for some reason but the object can be created).
See this page: Returning a value in constructor function of a class
Read it:-
Constructors don't get return values; they serve entirely to instantiate the class.
Without restructuring what you are already doing, you may consider using an exception here.
public function __construct ($identifier = NULL)
{
$this->emailAddress = $identifier;
$this->loadUser();
}
private function loadUser ()
{
// try to load the user
if (/* not able to load user */) {
throw new Exception('Unable to load user using identifier: ' . $this->identifier);
}
}
Now, you can create a new user in this fashion.
try {
$user = new User('user#example.com');
} catch (Exception $e) {
// unable to create the user using that id, handle the exception
}
If you're always expecting a string as a return value you can add the method __toString() then if you try to print that class it will return what you placed in there, only strings, and i can see that it's your case here, so i believe that should work for you..
public function __construct($username = null, $password = null){
$urlLogin = "{$this->apiHost}/login/$username";
$postData = sprintf("api_type=json&user=%s&passwd=%s",
$username,
$password);
$response = $this->runCurl($urlLogin, $postData);
if (count($response->json->errors) > 0){
return "login error";
} else {
$this->modHash = $response->json->data->modhash;
$this->session = $response->json->data->cookie;
return $this->modHash;
}
}
public function __toString(){
return $this->modeHash;
}
...
echo yourClass($username, $password); // will return yourClass->modeHash;
Unlike in other languages, in PHP you can explicitly call the constructor. It's just another function. It looks like the original author first decided to put some code that could fail in the constructor, then realized that he needs a way to rerun the initialization after a failure.
$result = $user->__construct($username, $password)
would actually work and you do get the return value. It's an ugly way to do things obviously.
In my opinion, it's not a good practice to have code that trigger side effects in the constructor. I would put the code in a separate function, with a name that clearly states what it does.
This is my first time shoving objects into Mongo - I am using PHP. I know that MongoDB adds the _id variable to an array while inserting using the MongoCollection::insert() function. the problem is this:
if I define a public variable named _id the variable remains NULL upon insert
class MognoTest {
public _id;
public foo;
public function __construct(){
$this->foo = 'bar';
}
}
$obj = new MongoTest();
$Mongo->collection->insert($obj);
var_dump($obj)
$found_obj = $Mongo->collection->findOne();
var_dump($found_obj);
The var_dump()s on $obj and $found_obj both return an object with _id=NULL. If I comment out _id in the class definition, the code runs properly and both var_dump()s print a MongoID.
I want to define in the class an _id to make my code clearer and make my code hinting on Netbeans work properly. Is there something I am overlooking?
Ok, so I got a solution, though i don't like it to be honest -- to me it seems inelegant.
class MognoTest {
public _id;
public foo;
public function __construct($load = NULL){
$this->foo = 'bar';
if ($load != NULL && isset($load['_id']))
$this->_id = $load['_id'];
}
public function insertPrep(){
if ($this->_id === NULL)
unset($this->_id);
}
}
//create object, prep, insert
$obj = new MongoTest();
$obj->insertPrep();
$Mongo->collection->insert($obj);
var_dump($obj)
//get object back and reload as object
$found_obj = $Mongo->collection->findOne();
$new_obj = new MongoTest($found_obj);
var_dump($new_obj);
I consider this solution inelegant because the programmer must now remember to add a line of code before each insert MongoTest::insertPrep(). I also tried adding a public function __insert() magic function but that didn't pan out :(
I still look for a better solution. But for anyone with this problem, here is a fix even if this fix isn't the elegant one I was hoping for.